-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
iteratorToArray Support #2894
iteratorToArray Support #2894
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @matinFT for taking the effort to submit a PR!
Unfortunately, I have to reject this particular PR for reasons I'll explain below. However, I'd like to encourage you to submit PRs again in the future. For this reason, I've decided to review your changes in detail, so you can already learn about some of the things that you need to take into account when contributing to Underscore. You'll find my comments after this post.
The reason that I'm rejecting this PR, is that Underscore needs to be consistent. If we're going to support iterables, then we should support them throughout the library rather than in just one function; in fact, iterables should become one of the main collection types next to array, object, map and set. This means that adding iterable support is a big breaking change. However, we recognize that it is very desirable to have, so it is planned for Underscore version 2.0. I dedicated #2147 as the central ticket for this plan. I previously mentioned the need for wholesale iterable support in #2448 (comment).
A similar PR that was rejected with similar reasoning is #2808.
@@ -0,0 +1,7 @@ | |||
function isIterable(obj) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you intended isIterable
to be a public function (i.e., mixed into the _
object), then you should list it in modules/index.js
. There should also be a comment before the function to explain its purpose. Take a look at a few other public modules to get a taste of how we write these comments.
If you intended isIterable
to be only an internal function instead, then you should start the name of the module with a leading underscore, i.e., modules/_isIterable.js
. In this case it would still be desirable to have a comment that explains the purpose of the function, but I'll readily admit that there are currently more internal functions where this is missing.
@@ -0,0 +1,7 @@ | |||
function isIterable(obj) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, you forgot export default
.
@@ -0,0 +1,7 @@ | |||
function isIterable(obj) { | |||
// checks for null and undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an unnecessary comment.
if (obj == null) { | ||
return false; | ||
} | ||
return typeof obj[Symbol.iterator] === 'function'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't safely assume that Symbol
or Symbol.iterator
is defined, because that will (needlessly!) break compatibility with older environments that only know ES3. Instead, we would use feature detection and then create an internal getIterator
function that simply returns undefined
in environments where these objects don't exist. You can see an example of this practice in modules/keys.js
, which uses Object.keys
but only if it exists.
A similar, but more subtle, consideration applies to typeof x === 'function'
. In some environments, this expression doesn't work as intended. This is why you should use _.isFunction
instead whenever possible. isFunction(x)
can also be minified much more compactly than typeof x === 'function'
.
So this line should have read like this:
return typeof obj[Symbol.iterator] === 'function'; | |
return isFunction(getIterator(obj)); |
if (obj == null) { | ||
return false; | ||
} | ||
return typeof obj[Symbol.iterator] === 'function'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given my previous comment, the entire body of this function would actually fit into a single expression:
if (obj == null) { | |
return false; | |
} | |
return typeof obj[Symbol.iterator] === 'function'; | |
return obj != null && isFunction(getIterator(obj)); |
Though getIterator
should probably check against null
and undefined
anyway, so you could actually leave out the null check entirely.
@@ -0,0 +1,9 @@ | |||
export default function iteratorToArray(iterator) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like with isIterable
, you should either add a descriptive comment and list this function in modules/index.js
if you intended this as a public function, or start the module name with _
otherwise. However, since this function seems to be purely an implementation detail of _.toArray
, it should probably be internal, and maybe it should even be defined inside modules/toArray.js
.
@@ -5,7 +5,8 @@ import isArrayLike from './_isArrayLike.js'; | |||
import map from './map.js'; | |||
import identity from './identity.js'; | |||
import values from './values.js'; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This blank line should stay.
@@ -16,5 +17,6 @@ export default function toArray(obj) { | |||
return obj.match(reStrSymbol); | |||
} | |||
if (isArrayLike(obj)) return map(obj, identity); | |||
else if (isIterable(obj)) return iteratorToArray(obj[Symbol.iterator]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When adding a feature to Underscore, you always also have to add tests to verify that the feature works as intended. This is especially important because this enables us to notice when a later change breaks a pre-existing feature.
I'm mentioning this here because _.toArray
is already a public function. If isIterable
or iteratorToArray
was public as well, though, you'd have to add tests for that, too.
No description provided.